Conversation
[FIX] #209: 중복 email 검증 오류 해결
[FEAT]: 코어 멤버 모집 폼 생성 및 파일 업로드 api 추가
[FEAT]: 코어 리쿠르트 지원자 조회 API 추가
Merge branch 'develop'
Merge branch 'develop'
Merge branch 'develop'
Merge branch 'develop'
Merge branch 'develop'
- AuthController에 /auth/{role} 엔드포인트 추가
- 요청된 role과 토큰 내 사용자 권한을 비교하여 접근 가능 여부 판단
- 상위 권한 사용자는 하위 권한 페이지 접근 허용, 그렇지 않으면 403 반환
- UserRole enum에 권한 순서 기반 비교 로직 추가 (ordinal 값 활용)
Feat: 권한 검증 엔드포인트 추가 및 UserRole 기반 비교 로직 개선
fix(core-attendance): Postgres unnest 파라미터 오류 해결 — 배열+캐스팅 적용
feat(core-attendance): refine controller endpoints & auth-aware team …
feat(core-attendance): 날짜 생성 권한 체크 추가
[FEAT] 사용자 권한 관리 기능 추가 및 인증/리크루팅 권한 구조 개선
feat(user-admin): 사용자 삭제 메서드 추가 (Controller 및 Service 계층)
fix(user-admin): User role 정렬 적용 pagination
fix(user-admin): 사용자 목록 정렬 및 권한별 접근 로직 개선
fix(manito): csv 파일 파싱 오류 수정
fix(manito): csv 파일 파싱 오류 수정
fix(manito): csv 파일 파싱 오류 수정
fix(manito): 미상 오류 수정중
fix(manito): 미상 오류 수정중
fix: 디버깅 설정 변경
fix: 디버깅 설정 변경
fix(manito): 핀 정규화 로직 의존성 추가
fix(security-config): 마니또 확인 페이지 필터링
fix(manito): 핀 확인 로그
fix(manito): Lob 삭제 및 Transactional 추가
fix(manito): unexpected token 해결
fix(manito): UI/UX 개선에 따른 변경사항 반영
워크스루이 PR은 마니또 시스템(세션 및 참여자 관리), 사용자 관리 기능, 인증·인가 강화를 도입합니다. 또한 출석 시스템에서 ORGANIZER/LEAD 역할 지원을 확대하고, 모집 모듈의 권한을 완화하며, 사용자 역할 계층 검증 메커니즘을 추가합니다. 변경 사항
예상 코드 리뷰 노력🎯 4 (복잡함) | ⏱️ ~50분 주의가 필요한 영역:
관련 가능성 있는 PR
제안 레이블
시
Pre-merge checks and finishing touches❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
CI status |
There was a problem hiding this comment.
Actionable comments posted: 13
🧹 Nitpick comments (12)
src/main/java/inha/gdgoc/domain/user/repository/UserRepository.java (1)
39-46: 페이지네이션 쿼리가 잘 구현되어 있습니다.JPQL 생성자 표현식을 사용한 DTO 프로젝션과 선택적 이름 필터링이 적절하게 구현되어 있습니다.
Line 44의
LIKE패턴에 앞쪽 와일드카드('%', :q, '%')가 있어 인덱스 활용이 제한됩니다. 사용자 테이블이 매우 큰 경우 전문 검색 솔루션(Full-text search) 고려를 권장합니다.src/main/java/inha/gdgoc/domain/core/attendance/controller/CoreAttendanceController.java (1)
71-71: 역할 검증 로직 중복을 헬퍼 메서드로 추출하는 것을 권장합니다.
me.getRole() == UserRole.LEAD && me.getTeam() != TeamType.HR패턴이 이 컨트롤러에서 6번 반복됩니다 (lines 71, 82, 95, 113, 120, 133). 가독성과 유지보수성을 위해 헬퍼 메서드로 추출하는 것이 좋습니다.🔎 헬퍼 메서드 추출 제안
/* ===== helpers ===== */ +private static boolean isLeadExceptHr(CustomUserDetails me) { + return me.getRole() == UserRole.LEAD && me.getTeam() != TeamType.HR; +} + private static TeamType requiredTeamFrom(CustomUserDetails me) { if (me.getTeam() == null) throw new IllegalArgumentException("LEAD 권한 토큰에 team 정보가 없습니다."); return me.getTeam(); }사용 예시:
-List<TeamResponse> list = (me.getRole() == UserRole.LEAD && me.getTeam() != TeamType.HR) ? ... +List<TeamResponse> list = isLeadExceptHr(me) ? ...src/main/java/inha/gdgoc/domain/user/controller/UserAdminController.java (2)
6-6: 사용되지 않는 import가 있습니다.
TeamType이 import되었지만 이 클래스에서 사용되지 않습니다.🔎 수정 제안
-import inha.gdgoc.domain.user.enums.TeamType;
43-43: 메시지 상수 사용을 권장합니다.다른 컨트롤러들(예:
CoreAttendanceController,RecruitMemberController)은 메시지 클래스에서 상수를 사용합니다. 일관성을 위해UserAdminMessage클래스를 만들어 사용하는 것이 좋습니다.// 예시: UserAdminMessage.java public class UserAdminMessage { public static final String USER_SUMMARY_LIST_RETRIEVED = "사용자 목록 조회 성공"; public static final String USER_ROLE_TEAM_UPDATED = "사용자 역할/팀 수정 성공"; public static final String USER_ROLE_UPDATED = "사용자 역할 수정 성공"; public static final String USER_DELETED = "사용자 삭제 성공"; }src/main/java/inha/gdgoc/domain/manito/service/ManitoAdminService.java (2)
225-225: 불필요한 문자열 인코딩/디코딩을 제거하세요.
sb.toString()은 이미 String을 반환하므로 UTF-8로 변환 후 다시 String으로 만드는 작업은 불필요합니다.🔎 수정 제안
- return new String(sb.toString().getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8); + return sb.toString();
193-195: 마니또 매칭에SecureRandom사용을 고려하세요.
Collections.shuffle()은 기본적으로ThreadLocalRandom을 사용하는데, 마니또 매칭의 예측 가능성이 중요하다면SecureRandom을 사용하는 것이 좋습니다. 주석에도 언급되어 있으니 보안 요구사항에 따라 결정하세요.🔎 수정 제안
+import java.security.SecureRandom; + List<ManitoAssignment> shuffled = new ArrayList<>(participants); - Collections.shuffle(shuffled); // 필요하면 SecureRandom 사용해도 됨 + Collections.shuffle(shuffled, new SecureRandom());src/main/java/inha/gdgoc/domain/user/service/UserAdminService.java (2)
166-169:@Transactional컨텍스트 내에서save()호출은 선택사항입니다.JPA dirty checking이 트랜잭션 커밋 시 변경사항을 자동으로 반영하므로, 명시적
save()호출이 반드시 필요하지는 않습니다. 코드 명확성을 위해 유지할 수 있지만, 불필요한 호출임을 인지하세요.
44-52:UserRoleenum과 CASE 문이 현재 동기화되어 있지만, 하드코딩된 역할 값으로 인한 유지보수 위험이 존재합니다.UserRole enum의 모든 값(GUEST, MEMBER, CORE, LEAD, ORGANIZER, ADMIN)이 CASE 문의 문자열과 일치합니다. 그러나 enum 상수가 변경되면 이 SQL 쿼리가 깨질 가능성이 있으므로, enum.name()을 활용하거나 상수로 관리하는 리팩토링을 고려하세요.
src/main/java/inha/gdgoc/domain/auth/controller/AuthController.java (2)
127-127: 메서드 이름responseResponseEntity가 의미를 명확히 전달하지 못합니다.
sendPasswordResetCode또는requestPasswordReset과 같이 기능을 명확히 나타내는 이름으로 변경하세요.🔎 수정 제안
- public ResponseEntity<ApiResponse<Void, Void>> responseResponseEntity(@RequestBody SendingCodeRequest sendingCodeRequest) { + public ResponseEntity<ApiResponse<Void, Void>> requestPasswordResetCode(@RequestBody SendingCodeRequest sendingCodeRequest) {
171-175: 인증 실패 시 예외를 던지는 방식을 고려하세요.직접
ResponseEntity를 구성하는 대신AuthException을 던지면 일관된 에러 응답 형식을 유지할 수 있습니다. 현재 방식도 동작하지만, 다른 엔드포인트와의 일관성을 위해 예외 기반 처리를 고려해 보세요.🔎 수정 제안
// 1) 인증 체크 if (me == null) { - return ResponseEntity.status(HttpStatus.UNAUTHORIZED) - .body(ApiResponse.error(GlobalErrorCode.UNAUTHORIZED_USER.getStatus() - .value(), GlobalErrorCode.UNAUTHORIZED_USER.getMessage(), null)); + throw new AuthException(UNAUTHORIZED_USER); }src/main/java/inha/gdgoc/domain/core/attendance/service/CoreAttendanceService.java (2)
226-227: 불필요한 UTF-8 인코딩 변환을 제거하세요.
sb.toString()은 이미 올바른 문자열을 반환하며, UTF-8 바이트로 변환 후 다시 문자열로 변환하는 것은 불필요합니다. 이 작업은 실제로 아무런 효과가 없으며,sb.toString()만 반환하면 됩니다.🔎 제안하는 수정
- return new String(sb.toString().getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8); + return sb.toString();
229-302: 전체 출석 매트릭스 CSV 생성 로직을 확인하세요.전반적인 로직은 올바르나, 다음 사항들을 확인해주세요:
불필요한 UTF-8 변환 (301번 라인):
buildSummaryCsv와 동일하게 불필요한 인코딩 변환이 있습니다.sb.toString()만 반환하면 됩니다.대용량 데이터 처리 시 성능: 사용자 수와 날짜 수가 많을 경우 (예: 사용자 1000명 × 날짜 50개), 메모리 사용량과 처리 시간이 증가할 수 있습니다. 259-261번 라인의 루프에서 각 날짜마다 별도의 쿼리를 실행하므로 N+1 문제가 발생할 수 있습니다.
🔎 제안하는 수정
1. UTF-8 변환 제거:
- return new String(sb.toString().getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8); + return sb.toString();2. 성능 개선 고려사항:
- 대용량 데이터를 처리해야 한다면, 배치 쿼리를 통해 모든 날짜의 출석 데이터를 한 번에 가져오는 것을 고려하세요.
- 사용자 수가 많을 경우 페이지네이션 또는 스트리밍 방식의 CSV 생성을 고려하세요.
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (34)
src/main/java/inha/gdgoc/domain/auth/controller/AuthController.java(8 hunks)src/main/java/inha/gdgoc/domain/core/attendance/controller/CoreAttendanceController.java(5 hunks)src/main/java/inha/gdgoc/domain/core/attendance/repository/AttendanceRecordRepository.java(1 hunks)src/main/java/inha/gdgoc/domain/core/attendance/service/CoreAttendanceService.java(6 hunks)src/main/java/inha/gdgoc/domain/core/recruit/controller/CoreRecruitController.java(2 hunks)src/main/java/inha/gdgoc/domain/manito/controller/ManitoAdminController.java(1 hunks)src/main/java/inha/gdgoc/domain/manito/controller/ManitoVerifyController.java(1 hunks)src/main/java/inha/gdgoc/domain/manito/controller/message/ManitoMessage.java(1 hunks)src/main/java/inha/gdgoc/domain/manito/dto/request/ManitoSessionCreateRequest.java(1 hunks)src/main/java/inha/gdgoc/domain/manito/dto/request/ManitoVerifyRequest.java(1 hunks)src/main/java/inha/gdgoc/domain/manito/dto/response/ManitoSessionResponse.java(1 hunks)src/main/java/inha/gdgoc/domain/manito/dto/response/ManitoVerifyResponse.java(1 hunks)src/main/java/inha/gdgoc/domain/manito/entity/ManitoAssignment.java(1 hunks)src/main/java/inha/gdgoc/domain/manito/entity/ManitoSession.java(1 hunks)src/main/java/inha/gdgoc/domain/manito/repository/ManitoAssignmentRepository.java(1 hunks)src/main/java/inha/gdgoc/domain/manito/repository/ManitoSessionRepository.java(1 hunks)src/main/java/inha/gdgoc/domain/manito/service/ManitoAdminService.java(1 hunks)src/main/java/inha/gdgoc/domain/manito/service/ManitoPinPolicy.java(1 hunks)src/main/java/inha/gdgoc/domain/manito/service/ManitoUserService.java(1 hunks)src/main/java/inha/gdgoc/domain/recruit/controller/RecruitMemberController.java(3 hunks)src/main/java/inha/gdgoc/domain/user/controller/UserAdminController.java(1 hunks)src/main/java/inha/gdgoc/domain/user/dto/request/UpdateRoleRequest.java(1 hunks)src/main/java/inha/gdgoc/domain/user/dto/request/UpdateUserRoleTeamRequest.java(1 hunks)src/main/java/inha/gdgoc/domain/user/dto/response/UserSummaryResponse.java(1 hunks)src/main/java/inha/gdgoc/domain/user/entity/User.java(1 hunks)src/main/java/inha/gdgoc/domain/user/enums/TeamType.java(2 hunks)src/main/java/inha/gdgoc/domain/user/enums/UserRole.java(1 hunks)src/main/java/inha/gdgoc/domain/user/repository/UserRepository.java(2 hunks)src/main/java/inha/gdgoc/domain/user/service/UserAdminService.java(1 hunks)src/main/java/inha/gdgoc/global/config/jwt/TokenProvider.java(5 hunks)src/main/java/inha/gdgoc/global/exception/BusinessException.java(1 hunks)src/main/java/inha/gdgoc/global/exception/GlobalErrorCode.java(1 hunks)src/main/java/inha/gdgoc/global/security/SecurityConfig.java(1 hunks)src/main/resources/application-prod.yml(1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-08-30T10:43:25.889Z
Learnt from: kaswhy
Repo: GDGoCINHA/24-2_GDGoC_Server PR: 207
File: src/main/java/inha/gdgoc/global/security/TokenAuthenticationFilter.java:31-39
Timestamp: 2025-08-30T10:43:25.889Z
Learning: The GDGoCINHA project has a consistent security architecture where public endpoints are handled in two layers: 1) SecurityConfig uses .permitAll() for paths like /api/v1/auth/**, /api/v1/game/**, /api/v1/apply/**, /api/v1/check/** and 2) TokenAuthenticationFilter excludes the same paths in shouldNotFilter(). This dual-layer approach is correct and intentional.
Applied to files:
src/main/java/inha/gdgoc/global/security/SecurityConfig.java
📚 Learning: 2025-08-30T10:43:25.889Z
Learnt from: kaswhy
Repo: GDGoCINHA/24-2_GDGoC_Server PR: 207
File: src/main/java/inha/gdgoc/global/security/TokenAuthenticationFilter.java:31-39
Timestamp: 2025-08-30T10:43:25.889Z
Learning: In the GDGoCINHA project, the following API paths are intentionally designed as public endpoints that should not require authentication: /api/v1/auth/, /api/v1/test/, /api/v1/game/, /api/v1/apply/, /api/v1/check/. These paths are correctly excluded from the TokenAuthenticationFilter in shouldNotFilter() method.
Applied to files:
src/main/java/inha/gdgoc/global/security/SecurityConfig.java
🧬 Code graph analysis (14)
src/main/java/inha/gdgoc/global/security/SecurityConfig.java (1)
src/main/java/inha/gdgoc/global/security/TokenAuthenticationFilter.java (1)
Override(24-47)
src/main/java/inha/gdgoc/global/exception/GlobalErrorCode.java (2)
src/main/java/inha/gdgoc/global/exception/GlobalExceptionHandler.java (1)
ExceptionHandler(116-126)src/main/java/inha/gdgoc/global/exception/ErrorCode.java (1)
ErrorCode(5-10)
src/main/java/inha/gdgoc/domain/manito/controller/ManitoVerifyController.java (2)
src/main/java/inha/gdgoc/domain/manito/controller/ManitoAdminController.java (1)
RestController(14-53)src/main/java/inha/gdgoc/domain/user/controller/UserController.java (2)
RequestMapping(25-60)PostMapping(43-50)
src/main/java/inha/gdgoc/domain/manito/dto/request/ManitoVerifyRequest.java (2)
src/main/java/inha/gdgoc/domain/core/attendance/dto/request/CreateDateRequest.java (1)
Getter(8-14)src/main/java/inha/gdgoc/domain/core/recruit/dto/request/CoreRecruitApplicationRequest.java (2)
Builder(47-62)Getter(10-63)
src/main/java/inha/gdgoc/domain/user/controller/UserAdminController.java (4)
src/main/java/inha/gdgoc/domain/user/controller/UserController.java (1)
RequestMapping(25-60)src/main/java/inha/gdgoc/domain/game/controller/GameUserController.java (1)
RequestMapping(19-41)src/main/java/inha/gdgoc/domain/study/controller/StudyController.java (1)
RequestMapping(38-108)src/main/java/inha/gdgoc/domain/user/service/UserService.java (1)
Slf4j(23-83)
src/main/java/inha/gdgoc/domain/core/attendance/controller/CoreAttendanceController.java (2)
src/main/java/inha/gdgoc/domain/core/attendance/controller/message/CoreAttendanceMessage.java (1)
CoreAttendanceMessage(3-22)src/main/java/inha/gdgoc/domain/core/attendance/dto/response/DaySummaryResponse.java (1)
Getter(19-27)
src/main/java/inha/gdgoc/domain/manito/controller/ManitoAdminController.java (4)
src/main/java/inha/gdgoc/domain/manito/controller/ManitoVerifyController.java (1)
RestController(16-34)src/main/java/inha/gdgoc/domain/resource/controller/FileUploadController.java (1)
RestController(16-34)src/main/java/inha/gdgoc/domain/user/controller/UserController.java (1)
RequestMapping(25-60)src/main/java/inha/gdgoc/domain/game/controller/GameUserController.java (1)
RequestMapping(19-41)
src/main/java/inha/gdgoc/domain/manito/repository/ManitoSessionRepository.java (5)
src/main/java/inha/gdgoc/domain/core/attendance/repository/MeetingRepository.java (1)
MeetingRepository(8-9)src/main/java/inha/gdgoc/domain/recruit/repository/RecruitMemberRepository.java (2)
RecruitMemberRepository(8-12)existsByStudentId(9-9)src/main/java/inha/gdgoc/domain/recruit/repository/AnswerRepository.java (1)
AnswerRepository(9-15)src/main/java/inha/gdgoc/domain/core/recruit/repository/CoreRecruitApplicationRepository.java (1)
CoreRecruitApplicationRepository(8-10)src/main/java/inha/gdgoc/domain/user/service/UserService.java (1)
Slf4j(23-83)
src/main/java/inha/gdgoc/domain/manito/repository/ManitoAssignmentRepository.java (2)
src/main/java/inha/gdgoc/domain/recruit/repository/RecruitMemberRepository.java (1)
RecruitMemberRepository(8-12)src/main/java/inha/gdgoc/domain/recruit/repository/AnswerRepository.java (1)
AnswerRepository(9-15)
src/main/java/inha/gdgoc/global/exception/BusinessException.java (4)
src/main/java/inha/gdgoc/domain/auth/exception/AuthException.java (2)
AuthException(8-10)AuthException(6-11)src/main/java/inha/gdgoc/domain/user/exception/UserException.java (2)
UserException(6-11)UserException(8-10)src/main/java/inha/gdgoc/domain/resource/exception/ResourceException.java (2)
ResourceException(8-10)ResourceException(6-11)src/main/java/inha/gdgoc/domain/study/exception/StudyException.java (2)
StudyException(6-11)StudyException(8-10)
src/main/java/inha/gdgoc/domain/manito/entity/ManitoSession.java (1)
src/main/java/inha/gdgoc/domain/manito/entity/ManitoAssignment.java (1)
Entity(10-60)
src/main/java/inha/gdgoc/domain/manito/entity/ManitoAssignment.java (1)
src/main/java/inha/gdgoc/domain/manito/entity/ManitoSession.java (1)
Getter(10-41)
src/main/java/inha/gdgoc/domain/user/service/UserAdminService.java (1)
src/main/java/inha/gdgoc/domain/user/controller/UserAdminController.java (1)
RequiredArgsConstructor(22-80)
src/main/java/inha/gdgoc/domain/auth/controller/AuthController.java (4)
src/main/java/inha/gdgoc/global/config/jwt/TokenProvider.java (1)
RequiredArgsConstructor(22-127)src/main/java/inha/gdgoc/domain/auth/service/AuthService.java (1)
Slf4j(27-138)src/main/java/inha/gdgoc/global/security/TokenAuthenticationFilter.java (1)
Slf4j(17-98)src/main/java/inha/gdgoc/domain/auth/controller/message/AuthMessage.java (1)
AuthMessage(3-11)
🔇 Additional comments (41)
src/main/java/inha/gdgoc/domain/user/enums/TeamType.java (1)
8-8: LGTM!새로운 HQ 팀 타입이 기존 패턴과 일관되게 추가되었습니다. enum 상수와 from() 메서드 모두 올바르게 업데이트되었습니다.
Also applies to: 22-22
src/main/java/inha/gdgoc/global/exception/BusinessException.java (1)
15-18: LGTM!커스텀 메시지를 지원하는 생성자가 올바르게 추가되었습니다. 기존 생성자와 함께 더 유연한 예외 처리를 가능하게 합니다.
src/main/java/inha/gdgoc/domain/user/enums/UserRole.java (2)
18-21: LGTM!권한 비교 로직이 올바르게 구현되었습니다. null 인자에 대해 false를 반환하는 것은 "권한 없음"을 의미하는 합리적인 기본 동작입니다.
26-35: LGTM!권한 계층 구조가 명확하게 정의되었습니다. enum 순서와 무관하게 rank를 중앙에서 관리하는 좋은 설계입니다.
src/main/java/inha/gdgoc/global/exception/GlobalErrorCode.java (1)
17-17: LGTM! 의미상 올바른 수정입니다.
FORBIDDEN_USER의 HTTP 상태를 404에서 403으로 변경한 것은 권한 부족 오류의 의미를 정확히 반영합니다. 다만 이는 API 소비자 입장에서 breaking change이므로, API 문서나 릴리스 노트에 명시하는 것을 권장합니다.src/main/java/inha/gdgoc/domain/user/dto/request/UpdateUserRoleTeamRequest.java (1)
6-9: LGTM!부분 업데이트를 위한 DTO 설계가 깔끔합니다. null을 "변경 없음"으로 처리하는 의미가 주석으로 명확히 표시되어 있습니다. 서비스 계층에서 이 의미를 올바르게 처리하는지는 User.java 리뷰에서 확인 요청했습니다.
src/main/java/inha/gdgoc/domain/user/entity/User.java (1)
134-135: serviceRole null 값 처리는 안전함
updateRoleAndTeam()메서드에서req.role()이 null일 때 기존 역할을 유지하는 로직이 있어서,changeRole()에는 항상 null이 아닌 값이 전달됩니다.updateUserRoleWithRules()메서드도newRole매개변수로 받은 유효한 역할값을 검증 후 전달하므로 문제없습니다.src/main/java/inha/gdgoc/domain/user/dto/request/UpdateRoleRequest.java (1)
6-8: LGTM!
@NotNull검증이 포함된 간결한 record DTO입니다. 역할 업데이트 요청에 적합한 구현입니다.src/main/java/inha/gdgoc/domain/manito/dto/response/ManitoSessionResponse.java (1)
7-11: LGTM!정적 팩토리 메서드 패턴을 사용한 깔끔한 DTO 구현입니다.
Instant를 사용한 타임스탬프 처리도 적절합니다.src/main/java/inha/gdgoc/domain/manito/dto/response/ManitoVerifyResponse.java (1)
7-11: LGTM!목적을 명확하게 설명하는 Javadoc과 함께 간결하게 구현된 DTO입니다.
src/main/java/inha/gdgoc/domain/user/dto/response/UserSummaryResponse.java (1)
6-14: LGTM!관리자 컨텍스트에서 사용자 요약 정보를 제공하기 위한 적절한 DTO 구현입니다. 필요한 필드들이 잘 정의되어 있습니다.
src/main/java/inha/gdgoc/domain/manito/repository/ManitoSessionRepository.java (1)
8-18: LGTM!리포지토리 인터페이스가 Spring Data JPA 규칙을 잘 따르고 있으며, 메서드 네이밍과 문서화가 명확합니다.
src/main/java/inha/gdgoc/domain/manito/service/ManitoUserService.java (1)
25-47: 검증 로직이 잘 구현되어 있습니다.메서드가 다음을 적절히 처리합니다:
- 세션 및 참가자 존재 여부 확인
- PIN 정규화 및 검증
- 암호화된 마니또 정보 존재 여부 확인
- 명확한 에러 메시지와 적절한 에러 코드 사용
트랜잭션 설정과 에러 처리가 올바르게 구현되어 있습니다.
src/main/java/inha/gdgoc/global/config/jwt/TokenProvider.java (2)
29-38: 새로운 토큰 생성 메서드가 적절합니다.
generateSelfSignupToken과generateGoogleLoginToken메서드가 기존generateRefreshToken패턴을 잘 따르고 있습니다.
63-77: 권한 구성 로직이 올바르게 개선되었습니다.역할(ROLE_)과 팀(TEAM_) 권한을 모두 포함하도록 변경되어 더 세밀한 권한 제어가 가능합니다. 선택적 팀 정보 처리도 적절합니다.
src/main/java/inha/gdgoc/domain/manito/dto/request/ManitoSessionCreateRequest.java (1)
8-15: LGTM!Record 기반 DTO가 간결하고 명확하며, 검증 제약조건이 적절하게 설정되어 있습니다.
src/main/java/inha/gdgoc/domain/manito/dto/request/ManitoVerifyRequest.java (1)
19-22: 검증 제약조건이 적절합니다.PIN 필드의 검증이 단계적으로 명확한 에러 메시지를 제공하도록 구성되어 있습니다.
@Size와@Pattern의 조합이 사용자에게 더 나은 검증 피드백을 제공합니다.src/main/java/inha/gdgoc/domain/user/repository/UserRepository.java (1)
48-48: Null 안전성 개선이 적절합니다.
@NotNull어노테이션 추가로 메서드 계약이 명확해졌습니다.src/main/java/inha/gdgoc/domain/core/attendance/repository/AttendanceRecordRepository.java (1)
29-36: 메서드 시그니처 변경이 적절하게 반영되었습니다.PostgreSQL 타입 안전성을 위한
CAST(:userIds AS bigint[])추가와 파라미터 변경(List<Long>→Long[])이 모두 올바르게 적용되었습니다. CoreAttendanceService의 호출 지점에서userIds.toArray(Long[]::new)으로 정확히 변환되어 메서드에 전달되고 있습니다.src/main/java/inha/gdgoc/domain/manito/controller/ManitoVerifyController.java (1)
23-33: 컨트롤러 구현이 적절합니다.REST 엔드포인트 구조가 명확하고, 검증 및 응답 처리가 올바르게 구현되어 있습니다.
verifyAndGetAssignment메서드가encryptedManitto의 존재 여부를 검증하므로 Line 27은 안전하고,name필드는 데이터베이스 수준에서nullable = false제약이 설정되어 있으므로 Line 28의getName()도 null을 반환할 수 없어 안전합니다.src/main/java/inha/gdgoc/domain/manito/repository/ManitoAssignmentRepository.java (1)
10-28: LGTM!다른 쿼리 메서드들은 Spring Data JPA 네이밍 컨벤션을 잘 따르고 있으며,
Optional반환 타입을 적절히 사용하여 null 안전성을 확보했습니다.findBySession_CodeAndStudentId를 통해 세션 레포지토리 없이 바로 조회할 수 있는 점도 좋은 설계입니다.src/main/java/inha/gdgoc/domain/core/attendance/controller/CoreAttendanceController.java (2)
54-66: LGTM!날짜 생성/삭제 엔드포인트에 대한
ORGANIZER/ADMIN역할 제한이 적절합니다. 클래스 레벨의@PreAuthorize를 메서드 레벨에서 더 제한적으로 오버라이드하는 패턴은 Spring Security 모범 사례를 따릅니다.
127-141: LGTM!새로운
summaryCsvAll엔드포인트가 기존 패턴을 일관되게 따르고 있습니다. Content-Disposition 헤더 설정과 CSV 응답 처리가 적절합니다.src/main/java/inha/gdgoc/domain/recruit/controller/RecruitMemberController.java (1)
88-88: 권한 표현식의 논리적 의도를 확인해 주세요.현재
@PreAuthorize표현식은 다음과 같이 해석됩니다:
LEAD,ORGANIZER,ADMIN역할 중 하나를 가진 사용자 또는- 역할에 관계없이 HR 팀에 속한 모든 사용자
이로 인해 HR 팀에 속한 일반
USER역할의 사용자도 이 민감한 엔드포인트에 접근할 수 있습니다. 의도된 동작이라면 무시하셔도 되지만,LEAD역할이면서 HR 팀인 경우만 허용하려면 표현식을 수정해야 합니다.의도된 동작인지 확인해 주세요:
- 현재: HR 팀원이면 역할 무관하게 접근 가능
- 대안:
(hasRole('LEAD') and principal.team == T(...).HR) or hasAnyRole('ORGANIZER','ADMIN')src/main/java/inha/gdgoc/domain/user/controller/UserAdminController.java (1)
59-59: HR 팀 권한 표현식 의도 확인 필요.
RecruitMemberController와 동일한 패턴입니다. HR 팀에 속한 일반USER역할도 역할 수정 권한을 가지게 됩니다. 의도된 동작인지 확인해 주세요.src/main/java/inha/gdgoc/domain/manito/controller/ManitoAdminController.java (1)
39-46: 파일 업로드 엔드포인트에 대한 검증 완료.다음 사항들이 이미 구현되어 있습니다:
파일 크기 제한: 모든 설정 파일(dev, local, prod)에서
max-file-size: 10MB,max-request-size: 12MB로 설정되어 있습니다.CSV 형식 검증:
ManitoAdminService.importParticipantsCsv()에서 빈 파일 확인, 헤더 형식 검증, 필수 컬럼 검증, CSV 이스케이프 처리, 필드 값 검증이 모두 서비스 레이어에서 수행됩니다.메모리 효율성: CSV 가져오기는
BufferedReader로 스트리밍 방식으로 처리되며, CSV 내보내기는StringBuilder로 빌드되어 일반적인 데이터 규모에서 메모리 문제가 없습니다.src/main/java/inha/gdgoc/domain/manito/entity/ManitoSession.java (2)
10-14: 엔티티 구조가 적절합니다.JPA 엔티티로서 적절한 어노테이션 구성입니다.
@NoArgsConstructor(access = AccessLevel.PROTECTED)로 JPA 프록시 생성을 지원하면서 외부 직접 생성을 방지합니다.
32-40: Builder 패턴과 변경 메서드 구현이 적절합니다.
code는 불변으로 유지하고title만 변경 가능하게 한 설계가 좋습니다. 세션 코드는 식별자 역할을 하므로 생성 후 변경을 막는 것이 올바른 접근입니다.src/main/java/inha/gdgoc/domain/manito/entity/ManitoAssignment.java (3)
10-14: 엔티티 구조와 제약 조건이 잘 설계되었습니다.세션-학번 조합에 대한 유니크 제약(
uq_manito_assignment_per_student)과 적절한 인덱스 설정으로 데이터 무결성과 쿼리 성능을 보장합니다.
20-22: LAZY 페치 전략이 적절합니다.
ManyToOne관계에FetchType.LAZY를 사용하여 불필요한 세션 데이터 로딩을 방지합니다.
49-59: 변경 메서드들이 도메인 캡슐화를 잘 지킵니다.Setter 대신 명시적인 변경 메서드(
changePinHash,changeEncryptedManitto,changeName)를 제공하여 도메인 로직의 의도를 명확히 합니다.src/main/java/inha/gdgoc/domain/manito/service/ManitoAdminService.java (1)
59-87: 그룹 크기 계산 알고리즘이 적절합니다.5~9명 그룹으로 분할하는 로직이 명확하게 구현되어 있으며, 최소 인원 검증도 포함되어 있습니다.
src/main/java/inha/gdgoc/domain/user/service/UserAdminService.java (3)
97-102: 권한 검증 로직이 적절합니다.편집자가 대상의 현재 역할과 새 역할 모두보다 상위 권한을 가져야 하는 검증이 올바르게 구현되어 있습니다.
104-143: 역할별 권한 규칙이 명확하게 구현되었습니다.ADMIN, ORGANIZER, LEAD 각각에 대한 권한 제약이 switch 표현식으로 잘 정리되어 있습니다. HR-LEAD의 팀 변경 예외 케이스도 적절히 처리됩니다.
163-172: HR-CORE 예외 경로가 적절히 구현되었습니다.HR팀 CORE 멤버가 GUEST를 MEMBER로 승격할 수 있는 특별 권한이 명확하게 분리되어 있습니다.
src/main/java/inha/gdgoc/domain/auth/controller/AuthController.java (2)
168-198: 역할/팀 검증 엔드포인트가 잘 구현되었습니다.
UserRole.hasAtLeast를 사용한 역할 검증과 ORGANIZER 이상은 팀 검증을 우회하는 로직이 적절합니다. OR 조건으로 최종 판정하는 것도 합리적입니다.
168-169: 유효하지 않은 역할 값에 대한 예외 처리는 이미 구현되어 있습니다.
@PathVariable UserRole role에 잘못된 문자열이 전달될 때 발생하는MethodArgumentTypeMismatchException은GlobalExceptionHandler에서@ExceptionHandler(MethodArgumentTypeMismatchException.class)(72-84줄)로 이미 처리되고 있습니다.src/main/java/inha/gdgoc/domain/core/attendance/service/CoreAttendanceService.java (4)
33-39: CSV 이스케이프 로직이 올바르게 구현되었습니다.RFC 4180 CSV 표준에 따라 null 처리, 특수 문자 감지, 따옴표 이스케이핑이 정확하게 구현되어 있습니다.
306-336: 헬퍼 메서드 문서화가 잘 되어 있습니다.Javadoc 주석 추가와 코드 포맷팅 개선이 적절하게 이루어졌습니다.
88-96: 프로젝트의 Java 버전은 17로 설정되어 있습니다.
.toList()메서드는 Java 16부터 도입되었으며, 프로젝트가 Java 17을 사용하고 있으므로 호환성 문제는 없습니다. 현재 코드는 최신 Java 문법을 적절하게 활용하고 있습니다.
76-76: UserRole.ORGANIZER 추가는 컨트롤러 레벨의 기존 권한 모델과 일치합니다.컨트롤러 클래스 레벨의
@PreAuthorize("hasAnyRole('LEAD','ORGANIZER','ADMIN')")에서 이미 ORGANIZER 역할이 허가되어 있습니다. 더욱이 컨트롤러 로직(line 71, 82, 113)에서 이들 서비스 메서드는 LEAD 사용자가 아닐 때(또는 HR 팀의 LEAD일 때) 호출되므로, ORGANIZER와 ADMIN이 의도된 대상입니다. 서비스 메서드에 UserRole.ORGANIZER를 추가하는 것은 기존 권한 정책과 일관성 있는 변경입니다.
| security = { @SecurityRequirement(name = "BearerAuth") } | ||
| ) | ||
| @PreAuthorize("hasRole('ADMIN')") | ||
| @PreAuthorize("hasAnyRole('LEAD', 'ORGANIZER', 'ADMIN')") |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# 1. CoreRecruitController.java 파일의 실제 내용 확인 (lines 50-100)
cat -n src/main/java/inha/gdgoc/domain/core/recruit/controller/CoreRecruitController.java | sed -n '50,100p'Repository: GDGoCINHA/24-2_GDGoC_Server
Length of output: 2669
🏁 Script executed:
# 2. CoreRecruitApplicationService 또는 관련 서비스에서 필터링 로직 확인
fd -e java CoreRecruitApplicationService -o CoreRecruitService --exec wc -l {} \; | head -5Repository: GDGoCINHA/24-2_GDGoC_Server
Length of output: 226
🏁 Script executed:
# 3. 전체 코드베이스에서 hasAnyRole 패턴 검색
rg -n "hasAnyRole.*LEAD.*ORGANIZER" --type=javaRepository: GDGoCINHA/24-2_GDGoC_Server
Length of output: 1814
🏁 Script executed:
# 4. UserRole 또는 Role enum 정의 확인
fd -e java | xargs grep -l "enum.*Role\|class.*Role" | head -5Repository: GDGoCINHA/24-2_GDGoC_Server
Length of output: 455
🏁 Script executed:
# 5. CoreRecruitController 파일의 전체 라인 수 확인
wc -l src/main/java/inha/gdgoc/domain/core/recruit/controller/CoreRecruitController.javaRepository: GDGoCINHA/24-2_GDGoC_Server
Length of output: 157
🏁 Script executed:
# CoreRecruitApplicationService 파일 찾기 및 내용 확인
fd -e java -path "*/service/*" | xargs grep -l "CoreRecruitApplicationService\|findApplicantsPage\|getApplicantDetail" | head -3Repository: GDGoCINHA/24-2_GDGoC_Server
Length of output: 239
🏁 Script executed:
# CoreRecruitApplicationService의 findApplicantsPage 메서드 구현 확인
rg -n "findApplicantsPage|getApplicantDetail" -A 10 --type=java -B 2Repository: GDGoCINHA/24-2_GDGoC_Server
Length of output: 5190
🏁 Script executed:
# CoreRecruitApplication 엔티티 구조 확인 (팀 연관성 있는지)
fd -e java CoreRecruitApplication --type=java | xargs cat | head -100Repository: GDGoCINHA/24-2_GDGoC_Server
Length of output: 242
🏁 Script executed:
# RecruitMemberController와 비교하기 위해 유사한 필터링 패턴 확인
rg -n "TeamType.*HR.*principal.team" --type=java -B 5 -A 5 | head -30Repository: GDGoCINHA/24-2_GDGoC_Server
Length of output: 3988
🏁 Script executed:
# CoreRecruitApplication 엔티티 구조 확인
find . -name "CoreRecruitApplication.java" -exec cat {} \;Repository: GDGoCINHA/24-2_GDGoC_Server
Length of output: 2021
🏁 Script executed:
# CoreRecruitApplicantDetailResponse와 CoreRecruitApplicantSummaryResponse 확인 (어떤 데이터 노출되는지)
rg -n "class CoreRecruitApplicant.*Response" -A 15Repository: GDGoCINHA/24-2_GDGoC_Server
Length of output: 53
🏁 Script executed:
# CoreRecruitApplication의 repository 쿼리 메서드 확인
rg -n "CoreRecruitApplicationRepository\|interface.*CoreRecruitApplication" -A 20Repository: GDGoCINHA/24-2_GDGoC_Server
Length of output: 53
지원자 조회 엔드포인트의 행 수준 보안 필터링이 누락되어 있습니다.
접근 권한이 ADMIN 전용에서 LEAD, ORGANIZER, ADMIN으로 확대되었으나, 서비스 계층에서 행 수준 필터링이 전혀 구현되지 않았습니다.
findApplicantsPage()(라인 42-47): 이름 검색만 수행, 팀 기반 필터링 없음getApplicantDetail()(라인 50-54): 직접 ID 조회, 권한 검증 없음CoreRecruitApplication엔티티는 "team" 필드를 보유하고 있으므로 지원자가 팀별로 범위가 지정되어야 함을 시사합니다
현재 상태에서 LEAD와 ORGANIZER 역할을 가진 사용자는 모든 팀의 모든 지원자 정보(학번, 전화번호, 이메일, 지원 동기, 강점 등 민감한 개인정보)에 제한 없이 접근할 수 있습니다.
팀별 또는 역할 컨텍스트에 따른 필터링 로직을 서비스 또는 저장소 계층에 추가해야 합니다.
🤖 Prompt for AI Agents
In
src/main/java/inha/gdgoc/domain/core/recruit/controller/CoreRecruitController.java
around lines 42-55, the controller was broadened to LEAD/ORGANIZER/ADMIN but did
not enforce row-level team filtering; update the service/repository layer to
apply team-based access control and modify findApplicantsPage() (lines ~42-47)
to add a team filter when the caller is LEAD or ORGANIZER (Admins bypass), and
modify getApplicantDetail() (lines ~50-54) to verify the current user's team(s)
against the applicant's team and return 403 (or throw AccessDenied) when not
allowed; obtain the caller's roles and team membership from SecurityContext (or
a CurrentUser service), push team-limited queries down to the repository (add
team constraint to the query or repository method), and add/adjust unit tests to
cover LEAD/ORGANIZER access to own-team applicants and denial for other-team
applicants.
| security = { @SecurityRequirement(name = "BearerAuth") } | ||
| ) | ||
| @PreAuthorize("hasRole('ADMIN')") | ||
| @PreAuthorize("hasAnyRole('LEAD', 'ORGANIZER', 'ADMIN')") |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find src -name "*CoreRecruitController*" -type fRepository: GDGoCINHA/24-2_GDGoC_Server
Length of output: 153
🏁 Script executed:
# Find the CoreRecruitController file
find . -name "CoreRecruitController.java" -type f 2>/dev/null | head -5
# Also search for any service methods related to applicant details
find . -name "*.java" -path "*/service/*" -type f 2>/dev/null | xargs grep -l "getApplicant" 2>/dev/null | head -10Repository: GDGoCINHA/24-2_GDGoC_Server
Length of output: 245
🏁 Script executed:
# Search for CoreRecruitController and getApplicantDetail method
find . -name "*.java" -type f | xargs grep -l "getApplicantDetail" 2>/dev/null | head -5Repository: GDGoCINHA/24-2_GDGoC_Server
Length of output: 245
🏁 Script executed:
# Check CoreRecruitController.java around line 92
cat -n ./src/main/java/inha/gdgoc/domain/core/recruit/controller/CoreRecruitController.java | sed -n '85,100p'Repository: GDGoCINHA/24-2_GDGoC_Server
Length of output: 784
🏁 Script executed:
# Check CoreRecruitApplicationService.java for getApplicantDetail method
grep -n "getApplicantDetail" ./src/main/java/inha/gdgoc/domain/core/recruit/service/CoreRecruitApplicationService.java -A 20Repository: GDGoCINHA/24-2_GDGoC_Server
Length of output: 393
🏁 Script executed:
# Also check if there's any filtering/authorization logic in the service
grep -n "teamId\|team\|authorization\|permission" ./src/main/java/inha/gdgoc/domain/core/recruit/service/CoreRecruitApplicationService.javaRepository: GDGoCINHA/24-2_GDGoC_Server
Length of output: 110
@PreAuthorize만으로는 불충분합니다. 서비스 레이어에 인가 검증이 필요합니다.
getApplicantDetail 메서드가 ID로 조회한 지원자에 대한 접근 권한을 검증하지 않습니다. 현재 구현:
- repository.findById(id)로 모든 지원자 조회 가능
- 요청자가 해당 지원자의 팀에 속하는지 확인 안 함
- 팀 기반 필터링 로직 없음
LEAD나 ORGANIZER가 임의의 ID로 모든 지원자의 상세 정보를 조회할 수 있는 심각한 보안 취약점입니다. 서비스 레이어에서 요청자의 팀과 지원자 소속 팀을 비교하여 접근을 제한해야 합니다.
🤖 Prompt for AI Agents
In
src/main/java/inha/gdgoc/domain/core/recruit/controller/CoreRecruitController.java
around line 92, the controller uses @PreAuthorize but the service layer does not
verify that the requesting user belongs to the same team as the applicant;
update the service method that loads applicant details to perform an explicit
authorization check: fetch the applicant by id, fetch the requester (from
SecurityContextHolder or by passing principal/userId into the service), compare
their team ids (allow if same team or if requester has ADMIN role), and if not
authorized throw an AccessDeniedException (or your app's equivalent); ensure the
controller passes the principal or userId into the service and add unit tests
for same-team, admin, and forbidden cases.
| @RestController | ||
| @RequestMapping("/api/v1/admin/manito") | ||
| @RequiredArgsConstructor | ||
| public class ManitoAdminController { |
There was a problem hiding this comment.
보안 취약점: 관리자 엔드포인트에 인증/인가가 없습니다.
이 컨트롤러는 /api/v1/admin/manito 경로에 있으며 관리자 기능(세션 생성, 참가자 업로드 등)을 제공하지만, @PreAuthorize 어노테이션이 없습니다. 누구나 이 엔드포인트에 접근할 수 있어 심각한 보안 취약점입니다.
다른 관리자 컨트롤러들(UserAdminController, CoreAttendanceController)은 적절한 권한 검사를 하고 있습니다.
🔎 수정 제안
+import org.springframework.security.access.prepost.PreAuthorize;
+
@RestController
@RequestMapping("/api/v1/admin/manito")
@RequiredArgsConstructor
+@PreAuthorize("hasAnyRole('ORGANIZER', 'ADMIN')")
public class ManitoAdminController {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| @RestController | |
| @RequestMapping("/api/v1/admin/manito") | |
| @RequiredArgsConstructor | |
| public class ManitoAdminController { | |
| import org.springframework.security.access.prepost.PreAuthorize; | |
| @RestController | |
| @RequestMapping("/api/v1/admin/manito") | |
| @RequiredArgsConstructor | |
| @PreAuthorize("hasAnyRole('ORGANIZER', 'ADMIN')") | |
| public class ManitoAdminController { |
🤖 Prompt for AI Agents
In src/main/java/inha/gdgoc/domain/manito/controller/ManitoAdminController.java
around lines 14 to 17, the controller exposing /api/v1/admin/manito lacks
authentication/authorization and must be restricted; add a class-level security
annotation (e.g., @PreAuthorize("hasRole('ADMIN')") or
@PreAuthorize("hasAuthority('ADMIN')") consistent with the project's convention)
or annotate each admin method similarly, ensure the necessary import and that
method security is enabled in the application (e.g., @EnableGlobalMethodSecurity
or equivalent), and mirror the same role/authority checks used in
UserAdminController/CoreAttendanceController so only authorized admin users can
access these endpoints.
| @PostMapping("/sessions") | ||
| public ResponseEntity<ApiResponse<ManitoSessionResponse, Void>> createSession( | ||
| @RequestBody ManitoSessionCreateRequest request | ||
| ) { | ||
| ManitoSessionResponse body = manitoAdminService.createSession(request); | ||
| return ResponseEntity.ok( | ||
| ApiResponse.ok("세션 생성 성공", body) | ||
| ); | ||
| } |
There was a problem hiding this comment.
요청 본문에 @Valid 어노테이션이 누락되었습니다.
ManitoSessionCreateRequest에 @NotBlank 등의 유효성 검사 어노테이션이 있을 경우, @Valid 없이는 검증이 수행되지 않습니다.
🔎 수정 제안
+import jakarta.validation.Valid;
+
@PostMapping("/sessions")
public ResponseEntity<ApiResponse<ManitoSessionResponse, Void>> createSession(
- @RequestBody ManitoSessionCreateRequest request
+ @RequestBody @Valid ManitoSessionCreateRequest request
) {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| @PostMapping("/sessions") | |
| public ResponseEntity<ApiResponse<ManitoSessionResponse, Void>> createSession( | |
| @RequestBody ManitoSessionCreateRequest request | |
| ) { | |
| ManitoSessionResponse body = manitoAdminService.createSession(request); | |
| return ResponseEntity.ok( | |
| ApiResponse.ok("세션 생성 성공", body) | |
| ); | |
| } | |
| import jakarta.validation.Valid; | |
| @PostMapping("/sessions") | |
| public ResponseEntity<ApiResponse<ManitoSessionResponse, Void>> createSession( | |
| @RequestBody @Valid ManitoSessionCreateRequest request | |
| ) { | |
| ManitoSessionResponse body = manitoAdminService.createSession(request); | |
| return ResponseEntity.ok( | |
| ApiResponse.ok("세션 생성 성공", body) | |
| ); | |
| } |
🤖 Prompt for AI Agents
In src/main/java/inha/gdgoc/domain/manito/controller/ManitoAdminController.java
around lines 29 to 37, the @RequestBody parameter is missing the @Valid
annotation so bean validation on ManitoSessionCreateRequest won't run; add the
@Valid annotation to the request parameter (e.g. public
ResponseEntity<ApiResponse<..., Void>> createSession(@Valid @RequestBody
ManitoSessionCreateRequest request)) and import the correct annotation
(jakarta.validation.Valid or javax.validation.Valid depending on project),
ensuring validation will be triggered.
| package inha.gdgoc.domain.manito.controller.message; | ||
|
|
||
| public class ManitoMessage { | ||
|
|
||
| } |
There was a problem hiding this comment.
빈 클래스의 목적을 명확히 해주세요.
이 클래스는 구현 내용이 없는 빈 클래스입니다. 의도적인 플레이스홀더인지, 추후 구현 예정인지, 아니면 실수로 커밋된 것인지 확인이 필요합니다. 사용되지 않는 코드는 제거하거나, 향후 구현이 필요한 경우 TODO 주석을 추가하는 것을 권장합니다.
이 클래스를 추적하기 위한 이슈를 생성하거나, 구현 코드를 생성하는 것을 도와드릴까요?
🤖 Prompt for AI Agents
In src/main/java/inha/gdgoc/domain/manito/controller/message/ManitoMessage.java
lines 1-5 there is an empty class with no implementation or comment; determine
its intended purpose and act accordingly: if it is a deliberate placeholder, add
a TODO comment describing the intended responsibilities, expected fields/methods
and a link or issue number, and annotate as @SuppressWarnings("unused") only if
referenced later; if it was committed accidentally and unused, delete the file
and remove any imports/usages; if implementation is pending, implement the
required fields/methods (or add an interface/record) consistent with other
message classes and update related tests; ensure commit message documents the
decision.
| assignmentRepository.flush(); | ||
|
|
||
| ManitoAssignment reloaded = assignmentRepository.findBySessionAndStudentId(session, studentId) | ||
| .orElseThrow(); | ||
|
|
||
| log.info("[MANITO] after save: studentId='{}', enc='{}'", reloaded.getStudentId(), reloaded.getEncryptedManitto()); |
There was a problem hiding this comment.
루프 내에서 flush() 호출은 성능 저하를 유발합니다.
flush()와 재조회가 각 레코드마다 실행되어 N+1 문제와 유사한 성능 저하가 발생합니다. 디버깅 목적이라면 제거하고, 필요시 트랜잭션 종료 시 한 번만 처리되도록 하세요.
🔎 수정 제안
assignment.changeEncryptedManitto(encryptedManitto);
-
- assignmentRepository.flush();
-
- ManitoAssignment reloaded = assignmentRepository.findBySessionAndStudentId(session, studentId)
- .orElseThrow();
-
- log.info("[MANITO] after save: studentId='{}', enc='{}'", reloaded.getStudentId(), reloaded.getEncryptedManitto());Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/main/java/inha/gdgoc/domain/manito/service/ManitoAdminService.java around
lines 264 to 269, the code calls assignmentRepository.flush() and then
re-queries each saved assignment inside a loop which causes per-record
flushing/reloads and significant performance degradation; remove the
per-iteration flush() and the immediate findBySessionAndStudentId(...) call,
instead rely on the managed entity state returned from the save operation (or
collect ids and perform a single bulk query after the loop) and perform a single
flush() (or let the transaction commit) after processing all records to avoid
N+1 behavior.
| try { | ||
| int asInt = Integer.parseInt(digits); | ||
| return String.format("%04d", asInt); | ||
| } catch (NumberFormatException e) { | ||
| throw new BusinessException(GlobalErrorCode.BAD_REQUEST, "PIN 형식이 올바르지 않습니다."); | ||
| } |
There was a problem hiding this comment.
4자리를 초과하는 PIN 입력 시 예상치 못한 동작 발생 가능
현재 구현에서 "12345"와 같이 4자리를 초과하는 숫자가 입력되면 String.format("%04d", 12345)는 "12345" (5자리)를 반환합니다. 이는 4자리 PIN 정책과 맞지 않을 수 있습니다.
최대 자릿수를 검증하거나, 마지막 4자리만 사용하도록 처리가 필요할 수 있습니다.
🔎 제안된 수정 사항
try {
int asInt = Integer.parseInt(digits);
+ if (asInt > 9999) {
+ throw new BusinessException(GlobalErrorCode.BAD_REQUEST, "PIN은 4자리 이하여야 합니다.");
+ }
return String.format("%04d", asInt);
} catch (NumberFormatException e) {
throw new BusinessException(GlobalErrorCode.BAD_REQUEST, "PIN 형식이 올바르지 않습니다.");
}🤖 Prompt for AI Agents
In src/main/java/inha/gdgoc/domain/manito/service/ManitoPinPolicy.java around
lines 22 to 27, the method currently parses the input and formats it which
allows inputs longer than 4 digits (e.g., "12345") to pass through; update the
validation to explicitly reject inputs longer than 4 characters (after trimming
and ensuring non-null) by throwing BusinessException with the same BAD_REQUEST
code, or alternatively decide to keep only the last 4 digits (pick one approach
consistently); implement the chosen behavior before parsing (check
digits.length() > 4 and throw or trim to last 4), keep existing
NumberFormatException handling for non-numeric input, and ensure leading zeros
are preserved by continuing to use String.format("%04d", parsedInt) for valid
4-or-fewer digit inputs.
| public ResponseEntity<ApiResponse<Void, Void>> updateRoleTeam( | ||
| @AuthenticationPrincipal CustomUserDetails me, | ||
| @PathVariable Long userId, | ||
| @RequestBody UpdateUserRoleTeamRequest req | ||
| ) { | ||
| userAdminService.updateRoleAndTeam(me, userId, req); | ||
| return ResponseEntity.ok(ApiResponse.ok("USER_ROLE_TEAM_UPDATED")); |
There was a problem hiding this comment.
@Valid 어노테이션이 누락되었습니다.
updateUserRole 메서드(line 64)에는 @Valid가 있지만, updateRoleTeam 메서드의 UpdateUserRoleTeamRequest에는 없습니다. 두 엔드포인트 모두 요청 본문 유효성 검사가 필요할 것으로 보입니다.
🔎 수정 제안
public ResponseEntity<ApiResponse<Void, Void>> updateRoleTeam(
@AuthenticationPrincipal CustomUserDetails me,
@PathVariable Long userId,
- @RequestBody UpdateUserRoleTeamRequest req
+ @RequestBody @Valid UpdateUserRoleTeamRequest req
) {🤖 Prompt for AI Agents
In src/main/java/inha/gdgoc/domain/user/controller/UserAdminController.java
around lines 49 to 55, the request body parameter for updateRoleTeam is missing
the @Valid annotation so the UpdateUserRoleTeamRequest won't be validated; add
the @Valid annotation to the @RequestBody parameter (and ensure the
javax.validation.Valid import is present) so the request is validated the same
way as updateUserRole.
| "/api/v1/fileupload", | ||
| "/api/v1/manito/verify") |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# TokenAuthenticationFilter 파일 위치 찾기
find . -name "TokenAuthenticationFilter.java" -type f
# shouldNotFilter 메서드 내용 확인
rg -A 20 "shouldNotFilter" src/main/java/inha/gdgoc/global/security/TokenAuthenticationFilter.java
# manito와 fileupload 경로 확인
rg -n "manito|fileupload" src/main/java/inha/gdgoc/global/security/TokenAuthenticationFilter.javaRepository: GDGoCINHA/24-2_GDGoC_Server
Length of output: 1081
🏁 Script executed:
#!/bin/bash
# TokenAuthenticationFilter 전체 파일 읽기
cat -n src/main/java/inha/gdgoc/global/security/TokenAuthenticationFilter.javaRepository: GDGoCINHA/24-2_GDGoC_Server
Length of output: 4264
TokenAuthenticationFilter에 새 엔드포인트 추가 필수
/api/v1/manito/verify와 /api/v1/fileupload 엔드포인트가 SecurityConfig의 permitAll()에는 추가되었으나, TokenAuthenticationFilter의 shouldNotFilter() 메서드에는 추가되지 않았습니다. 프로젝트의 보안 아키텍처 패턴에 따르면 양쪽 모두에서 처리되어야 하므로, TokenAuthenticationFilter 46행 다음에 다음을 추가하세요:
|| uri.startsWith("/api/v1/manito/verify")
|| uri.equals("/api/v1/fileupload");
현재 상태에서는 이 경로들이 SecurityConfig에서는 허용되지만 필터에서는 여전히 인증을 시도하게 됩니다.
🤖 Prompt for AI Agents
In src/main/java/inha/gdgoc/global/security/SecurityConfig.java around lines
52-53, the two endpoints were added to permitAll() but not to
TokenAuthenticationFilter.shouldNotFilter; update TokenAuthenticationFilter
(after line 46) to include the additional exclusions by adding checks for
uri.startsWith("/api/v1/manito/verify") and uri.equals("/api/v1/fileupload") so
those paths are skipped by the filter (place them in the existing OR-chain in
shouldNotFilter()).
| org.hibernate.orm.jdbc.bind: trace | ||
| org.hibernate.type: trace |
There was a problem hiding this comment.
프로덕션 환경에서 trace 레벨 로깅은 위험합니다.
프로덕션 환경에서 Hibernate 로깅을 trace 레벨로 설정하면 다음과 같은 심각한 문제가 발생합니다:
- 성능 저하: trace 로깅은 방대한 양의 로그를 생성하여 CPU, I/O, 디스크 공간을 과도하게 소비합니다.
- 보안/컴플라이언스 위험:
org.hibernate.orm.jdbc.bind: trace는 SQL 파라미터 바인딩을 로그에 기록하므로, 사용자 이메일, 비밀번호, 개인정보 등 민감한 데이터가 로그에 노출될 수 있습니다. - 운영 리스크: 로그 볼륨 급증으로 디스크 공간 부족, 로그 분석 어려움 등의 문제가 발생할 수 있습니다.
프로덕션 환경에서는 INFO 또는 WARN 레벨을 권장하며, 필요시 DEBUG까지만 사용하는 것이 안전합니다.
🔎 권장 수정사항
옵션 1 (권장): 프로덕션에서는 낮은 로그 레벨 사용
logging:
level:
org.hibernate.SQL: debug
- org.hibernate.orm.jdbc.bind: trace
- org.hibernate.type: trace
+ org.hibernate.orm.jdbc.bind: off
+ org.hibernate.type: off옵션 2: 상세 로깅이 필요한 경우 개발/스테이징 환경에서만 활성화
프로덕션(application-prod.yml)에서는 제거하고, 개발 환경(application-dev.yml)에서만 trace 레벨을 사용하세요.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| org.hibernate.orm.jdbc.bind: trace | |
| org.hibernate.type: trace | |
| org.hibernate.orm.jdbc.bind: off | |
| org.hibernate.type: off |
🤖 Prompt for AI Agents
src/main/resources/application-prod.yml around lines 53-54: the production
config currently sets org.hibernate.orm.jdbc.bind and org.hibernate.type to
trace which can leak sensitive data and degrade performance; change these two
entries to a safer level (e.g., INFO or WARN) or remove them entirely from
application-prod.yml and instead place trace-level settings only in
application-dev.yml/application-staging.yml; ensure no JDBC bind/parameter trace
logging remains enabled in production.
📌 연관된 이슈
✨ 작업 내용
💬 리뷰 요구사항(선택)
Summary by CodeRabbit
릴리스 노트
새로운 기능
접근 권한 확대
버그 수정
✏️ Tip: You can customize this high-level summary in your review settings.